<?php
/** 
 * 
 *
 * @package api
 * @subpackage collections
 * @author $Author$
 * 
 * @version $Id: AbstractMap.php 14 2006-11-28 22:05:19Z rdff3 $
 */
 
/**
 *
 */ 
require_once( "lang/Object.php" );
require_once( "util/AbstractCollection.php" );
require_once( "util/AbstractSet.php" );
require_once( "util/Map.php" );

/**
 * AbstractMap
 *
 * This class provides a skeletal implementation of the Map  interface, to minimize the effort required to implement this interface.
 * 
 * To implement an unmodifiable map, the programmer needs only to extend this class and provide an implementation for the entrySet
 * method, which returns a set-view of the map's mappings. Typically, the returned set will, in turn, be implemented atop 
 * AbstractSet. This set should not support the add or remove methods, and its iterator should not support the remove method.
 *
 * To implement a modifiable map, the programmer must additionally override this class's put method (which otherwise throws 
 * an UnsupportedOperationException), and the iterator returned by entrySet().iterator() must additionally implement its remove 
 * method.
 *
 * The programmer should generally provide a void (no argument) and map constructor, as per the recommendation in the Map 
 * interface specification.
 *
 * The documentation for each non-abstract methods in this class describes its implementation in detail. Each of these methods may 
 * be overridden if the map being implemented admits a more efficient implementation.
 *
 * This class is a member of the PHP Collections Framework. 
 *
 * @package api
 * @subpackage collections
 * @author $Author$
 * @version $Id: AbstractMap.php 14 2006-11-28 22:05:19Z rdff3 $
 */
abstract class AbstractMap extends Object implements Map {
	protected $key_set;
	protected $values;
	
	/**
	 * Removes all mappings from this map.
	 *
	 * This implementation calls entrySet().clear(). Note that this implementation throws an UnsupportedOperationException if 
	 * the entrySet does not support the clear operation.
	 *
	 * @throws UnsupportedOperationException - clear is not supported by this map.
	 */
	public function clear() {
		return $this->entrySet()->clear();
	}
	
	/**
	 * Returns true if this map contains a mapping for the specified key.
	 *
	 * This implementation iterates over entrySet() searching for an entry with the specified key. If such an entry is found, true  
	 * is returned. If the iteration terminates without finding such an entry, false is returned. Note that this implementation 
	 * requires linear time in the size of the map; many implementations will override this method.
	 *
	 * @param Object $key key whose presence in this map is to be tested.
	 * @returns boolean true if this map contains a mapping for the specified key.
	 * @throws NullPointerException - if the key is null and this map does not permit null keys.
	 */
	public function containsKey( Object $key ) {
		if (is_null($key))
			throw new NullPointerException();
	
		$iterator = $this->entrySet()->iterator();
		while ($iterator->hasNext()) {
			$next = $iterator->getNext();
			$k = $next->getKey();
			if ($key->equals($k))
				return true;
		}
		return false;
	}
	
	/**
	 * Returns true if this map maps one or more keys to this value.
	 *
	 * Returns true if this map maps one or more keys to this value. More formally, returns true if and only if this map contains 
	 * at least one mapping to a value v such that (value==null ? v==null : value.equals(v)). This operation will probably require 
	 * time linear in the map size for most implementations of map.
	 *
	 * This implementation iterates over entrySet() searching for an entry with the specified value. If such an entry is found, true 
	 * is returned. If the iteration terminates without finding such an entry, false is returned. Note that this implementation 
	 * requires linear time in the size of the map. 
	 *
	 * @param Object $value value whose presence in this map is to be tested.
	 * @returns boolean true if this map maps one or more keys to this value.
	 */
	public function containsValue( Object $value ) {
		$iterator = $this->entrySet()->iterator();
		while ($iterator->hasNext()) {
			$next = $iterator->getNext();
			$v = $next->getValue();
			if ($value == null and $v == null)
				return true;
			elseif ($value->equals($v))
				return true;
		}
		return false;
	}
	
	/**
	 * Returns a set view of the mappings contained in this map.
	 *
	 * Returns a set view of the mappings contained in this map. Each element in this set is a Map.Entry. The set is backed by the 
	 * map, so changes to the map are reflected in the set, and vice-versa. (If the map is modified while an iteration over the set 
	 * is in progress, the results of the iteration are undefined.) The set supports element removal, which removes the corresponding
	 * entry from the map, via the CollectionIterator.remove, Set.remove, removeAll, retainAll and clear operations. It does not support the 
	 * add or addAll operations.
	 *
	 * @returns Set a set view of the mappings contained in this map.
	 */
	public abstract function entrySet();
	
	/**
	 * Compares the specified object with this map for equality.
	 *
	 * Compares the specified object with this map for equality. Returns true if the given object is also a map and the two 
	 * maps represent the same mappings. More formally, two maps t1 and t2 represent the same mappings if 
	 * t1.keySet().equals(t2.keySet()) and for every key k  in t1.keySet(),  (t1.get(k)==null ? t2.get(k)==null : 
	 * t1.get(k).equals(t2.get(k))) . This ensures that the equals method works properly across different implementations of 
	 * the map interface.
	 * 
	 * This implementation first checks if the specified object is this map; if so it returns true. Then, it checks if the 
	 * specified object is a map whose size is identical to the size of this set; if not, it returns false. If so, it iterates 
	 * over this map's entrySet collection, and checks that the specified map contains each mapping that this map contains. If 
	 * the specified map fails to contain such a mapping, false is returned. If the iteration completes, true is returned. 
	 *
	 * @param Object $o object to be compared for equality with this map.
	 * @returns boolean true if the specified object is equal to this map.
	 */
	public function equals( Object $o ) {
		if ($this === $o)
			return true;
		elseif ($this->size() != $o->size())
			return false;
		else {
			$iterator = $this->entrySet()->iterator();
			while ($iterator->hasNext()) {
				if ( !$o->entrySet()->contains( $iterator->getNext() ) )
					return false;
			}
			return true;
		}
	
	}
	
	/**
	 * Returns the value to which this map maps the specified key.
	 *
	 * Returns the value to which this map maps the specified key. Returns null if the map contains no mapping for this key. A 
	 * return value of null does not necessarily indicate that the map contains no mapping for the key; it's also possible that the 
	 * map explicitly maps the key to null. The containsKey operation may be used to distinguish these two cases.
	 *
	 * This implementation iterates over entrySet() searching for an entry with the specified key. If such an entry is found, the 
	 * entry's value is returned. If the iteration terminates without finding such an entry, null is returned. Note that 
	 * this implementation requires linear time in the size of the map; many implementations will override this method. 
	 *
	 * @param Object $key key whose associated value is to be returned.
	 * @returns Object the value to which this map maps the specified key.
	 * @throws NullPointerException - if the key is null and this map does not permit null keys.
	 */
	public function get( Object $key ) {
		if (is_null($key))
			throw new NullPointerException();
	
		$iterator = $this->entrySet()->iterator();
		while ($iterator->hasNext()) {
			$next = $iterator->getNext();
			$k = $next->getKey();
			if ($key->equals($k))
				return $key;
		}
		return null;
	}
	
	/**
	 * Returns the hash code value for this map. 
	 *
	 * Returns the hash code value for this map. The hash code of a map is defined to be the sum of the hash codes of each entry 
	 * in the map's entrySet() view. This ensures that t1.equals(t2)  implies that t1.hashCode()==t2.hashCode() for any two maps 
	 * t1 and t2, as required by the general contract of Object.hashCode.
	 * 
	 * This implementation iterates over entrySet(), calling hashCode on each element (entry) in the Collection, and adding up 
	 * the results. 
	 *
	 * @returns int the hash code value for this map.
	 */
	public function hashCode() {
		$h = 0;
		$i = $this->entrySet()->iterator();
		while ($i->hasNext() )
			$h += $i->getNext()->hashCode();
		return $h;
	}
	
	/**
	 * Returns true if this map contains no key-value mappings.
	 *
	 * This implementation returns size() == 0.
	 *
	 * @returns boolean true if this map contains no key-value mappings.
	 */
	public function isEmpty() {
		return ($this->size() == 0);
	}
	
	/**
	 * Returns a Set view of the keys contained in this map.
	 *
	 * Returns a Set view of the keys contained in this map. The Set is backed by the map, so changes to the map are reflected 
	 * in the Set, and vice-versa. (If the map is modified while an iteration over the Set is in progress, the results of the 
	 * iteration are undefined.) The Set supports element removal, which removes the corresponding entry from the map, via the 
	 * CollectionIterator.remove, Set.remove, removeAll retainAll, and clear operations. It does not support the add or addAll operations.
	 * 
	 * This implementation returns a Set that subclasses AbstractSet. The subclass's iterator method returns a "wrapper object" 
	 * over this map's entrySet() iterator. The size method delegates to this map's size method and the contains method delegates 
	 * to this map's containsKey method.
	 * 
	 * The Set is created the first time this method is called, and returned in response to all subsequent calls. No synchronization 
	 * is performed, so there is a slight chance that multiple calls to this method will not all return the same Set. 
	 *
	 * @returns Set a Set view of the keys contained in this map.
	 */
	public function keySet() {
		if ( !$this->key_set ) {
			$this->key_set = new AbstractMap_KeySet( $this );
			return $this->key_set;
		}
		return $this->key_set;
	}
	
	/**
	 * Associates the specified value with the specified key in this map.
	 *
	 * If the map previously contained a mapping for this key, the old value is replaced.
	 *
	 * This implementation always throws an UnsupportedOperationException. 
	 *
	 * @param Comparable $key key with which the specified value is to be associated.
	 * @param Object $value value to be associated with the specified key.
	 * @returns mixed previous value associated with specified key, or null  if there was no mapping for key. (A null return can 
	 * 		also indicate that the map previously associated null  with the specified key, if the implementation supports null values.) 
	 * @throws UnsupportedOperationException - if the put operation is not supported by this map. 
	 * @throws ClassCastException - if the class of the specified key or value prevents it from being stored in this map. 
	 * @throws IllegalArgumentException - if some aspect of this key or value * prevents it from being stored in this map. 
	 * @throws NullPointerException - if this map does not permit null keys or values, and the specified key or value is null.
	 */
	public function put( Object $key, Object $value ) {
		throw new UnsupportedOperationException();
	}
	
	/**
	 * Copies all of the mappings from the specified map to this map.
	 *
	 * Copies all of the mappings from the specified map to this map (optional operation). These mappings will replace any mappings 
	 * that this map had for any of the keys currently in the specified map.
	 * 
	 * This implementation iterates over the specified map's entrySet() collection, and calls this map's put operation once for 
	 * each entry returned by the iteration.
	 * 
	 * Note that this implementation throws an UnsupportedOperationException if this map does not support the put operation and 
	 * the specified map is nonempty. 
	 *
	 * @param Map $t mappings to be stored in this map.
	 * @throws UnsupportedOperationException - if the putAll operation is not supported by this map. 
	 * @throws ClassCastException - if the class of a key or value in the specified map prevents it from being stored in this map. 
	 * @throws IllegalArgumentException - if some aspect of a key or value in the specified map prevents it from being stored in 
	 *		this map. 
	 * @throws NullPointerException - if the specified map is null, or if this map does not permit null keys or values, and 
	 *		the specified map contains null keys or values.
	 */
	public function putAll( Map $t ) {
		$iterator = $t->entrySet()->iterator();
		while ($iterator->hasNext()) {
			$this->put($iterator->getNext());
		}
	
	}
	
	/**
	 * Removes the mapping for this key from this map if present (optional operation).
	 *
	 * This implementation iterates over entrySet() searching for an entry with the specified key. If such an entry is found, 
	 * its value is obtained with its getValue operation, the entry is removed from the Collection (and the backing map) with 
	 * the iterator's remove operation, and the saved value is returned. If the iteration terminates without finding such an 
	 * entry, null is returned. Note that this implementation requires linear time in the size of the map; many implementations 
	 * will override this method.
	 * 
	 * Note that this implementation throws an UnsupportedOperationException if the entrySet iterator does not support the remove 
	 * method and this map contains a mapping for the specified key. 
	 *
	 * @param Object $key key whose mapping is to be removed from the map.
	 * @param Object $value value to be associated with the specified key.
	 * @returns mixed previous value associated with specified key, or null  if there was no entry for key. (A null return can 
	 * 		also indicate that the map previously associated null  with the specified key, if the implementation supports null values.)
	 * @throws UnsupportedOperationException - if the put operation is not supported by this map. 
	 */
	public function remove( Object $key ) {
		$iterator = $this->entrySet()->iterator();
		while ($iterator->hasNext()) {
			$next = $iterator->getNext();
			$k = $next->getKey();
			if ($key->equals($k))
				return $iterator->remove()->getValue();
		}
		return null;
	}
	
	/**
	 * Returns the number of key-value mappings in this map.
	 *
	 * This implementation returns entrySet().size()
	 *
	 * @returns int the number of key-value mappings in this map.
	 */
	public function size() {
		return $this->entrySet()->size();
	}
	
	/**
	 * Returns a collection view of the values contained in this map.
	 *
	 * Returns a collection view of the values contained in this map. The collection is backed by the map, so changes to the map 
	 * are reflected in the collection, and vice-versa. (If the map is modified while an iteration over the collection is in progress,
	 * the results of the iteration are undefined.) The collection supports element removal, which removes the corresponding entry 
	 * from the map, via the CollectionIterator.remove, Collection.remove, removeAll, retainAll and clear operations. It does not support the 
	 * add or addAll operations.
	 * 
	 * This implementation returns a collection that subclasses abstract collection. The subclass's iterator method returns a 
	 * "wrapper object" over this map's entrySet() iterator. The size method delegates to this map's size method and the contains 
	 * method delegates to this map's containsValue method.
	 * 
	 * The collection is created the first time this method is called, and returned in response to all subsequent calls. No
	 * synchronization is performed, so there is a slight chance that multiple calls to this method will not all return the same 
	 * Collection. 
	 *
	 * @returns Collection a collection view of the values contained in this map.
	 */
	public function values() {
		if ( !$this->value_set ) {
			$this->value_set = new AbstractMap_ValueCollection( $this );
			return $this->value_set;
		}
		return $this->value_set;
	}
}


/**
 * Class AbstractMap_ValueCollection
 *
 * Internal structure representing an abstract maps' collection of values.
 *
 * @package api
 * @subpackage collections
 * @author $Author$
 * @version $Id: AbstractMap.php 14 2006-11-28 22:05:19Z rdff3 $
 * @internal
 */
class AbstractMap_ValueCollection extends AbstractCollection {
	private $map;
	
	public function __construct( AbstractMap $map ) {
		$this->map = $map;
	}
	
	public function iterator() {
		return $this->map->entrySet()->iterator();  // need to specify which iterator... Value CollectionIterator...
	}
	
	public function size() {
		return $this->map->size();
	}
	
	public function contains( Object $o ) {
		return $this->map->containsKey($o);
	}
}
/**
 * Class AbstractMap_KeySet
 *
 * Internal structure representing an abstract map's keySet.
 *
 * @package api
 * @subpackage collections
 * @author $Author$
 * @version $Id: AbstractMap.php 14 2006-11-28 22:05:19Z rdff3 $
 * @internal
 */
class AbstractMap_KeySet extends AbstractSet {
	private $map;
	
	public function __construct( AbstractMap $map ) {
		$this->map = $map;
	}
	
	public function iterator() {
		return $this->map->entrySet()->iterator();  // need to specify which iterator... Key CollectionIterator...
	}
	
	public function size() {
		return $this->map->size();
	}
	
	public function contains( Object $o ) {
		return $this->map->containsKey($o);
	}
}



?>